/*
 * Pass2.cpp
 *
 *  Created on: May 13, 2012
 *      Author: miro
 */

#include "Pass2.h"

#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <iomanip>
#include <locale>
#include <fstream>
#include "LocationCounter.h"
#include "symbolChecker.h"
#include "LabelObject.h"
#include "Line.h"
#include "FileValidation.h"
using namespace std;

using namespace counter;

Pass2::Pass2(map<string, LabelObject> symbolTable, map<string, int> litPool,
                vector<Line*> input) {
        this->input = input;
        this->litPool = litPool;
        this->symbolTable = symbolTable;
        this->MAX_HEX_20 = 100000;
        this->MAX_DEC_20 = 1048576;
        this->baseValue = -1;
        this->checker = symbolChecker::getInstance();
        baseValue = -1;
}

Pass2::Pass2() {
        // TODO Auto-generated constructor stub
        this->MAX_HEX_20 = 100000;
        this->MAX_DEC_20 = 1048576;
        this->MAX_DEC_12 = 4096;
        this->MAX_HEX_12 = 1000;
        this->MAX_DEC_11 = 2048;
        this->MAX_HEX_11 = 800;
        this->checker = symbolChecker::getInstance();
        baseValue = -1;
}

Pass2::~Pass2() {
        // TODO Auto-generated destructor stub
}
int Pass2::calcFormat1(int n) {
        int opcode = 0;
        opcode = checker->getSymbolOpCode(input[n]->opcode);
        return opcode;
}
int Pass2::calcDisp(int lineNo) {

        Line *current = input[lineNo];

        int pc = input[lineNo + 1]->address;
        int disp = 0;
        string operand = current->operand;
        // remove #
        if (operand[0] == '#') {
                operand = operand.substr(1, operand.size());
        }
        if (checkIndexed(lineNo)) {
                operand = operand.substr(0, operand.size() - 2);
        }
        if (flag.e == 1) {
                disp = calcFormat4(lineNo, operand);
        } else {
                disp = calcFormat3(lineNo, operand, pc);
        }

//      } else if (flag.n == 1 && flag.i == 1) {
//              disp = calcFormat3(lineNo, current->operand, pc);
//      } else if (flag.n == 1) {
//              disp = calcFormat3(lineNo, current->operand, pc);
//      } else if (flag.i == 1) {
//              disp = calcFormat3(lineNo, current->operand, pc);
//      }

        return disp;
}

int Pass2::calcFormat3(int lineNo, string operand, int pc) {
        int address = -1;

// remove #

// try to parse the operand (#C)

        if (operand.compare("*") == 0) {
                address = pc;
        }

        address = calcForBaseAndPC(lineNo, operand, pc);
        if (address == -1) {
                std::stringstream ss(operand);
                ss >> hex >> address;
                if (ss.fail()) {
                        // not an integer
                        address = -1;
                }
                if (address > MAX_HEX_12) {
                        input[lineNo]->error = "**Error out of memory";
                }
        }
        if (address == -1) {
                input[lineNo]->error = "**Symbol not defined";
        }

        return address;
}
void Pass2::writeFile(char* fileName) {
        FileValidation f;
        vector<Line*> lines = f.readFromFile(fileName);
        LocationCounter *l = new LocationCounter(lines);
        l->generateOutput();
        this->symbolTable = l->symbolTable;
        this->litPool = l->litPool;
        this->input = l->output;

        l->printTable();
        l->writeFile();
        setObjectProgram();
        ofstream myfile;
        myfile.open("output2.txt");
        myfile
                        << "LineNo\t\tLOCCTR\t\tLabel\t\tOpcode\t\tOperand\t\tComment\t\tObjectcode\n";
        string s = "";
//      cout << input.size();
        for (int i = 0; i < l->output.size(); ++i) {

                Line *curr = l->output[i];
//              cout << curr->label;
//              cout << curr->opcode;li
//              cout << " ";
                cout << hex << curr->address;
                cout << "\n";
//              cout << "\n";
//              cout << curr->error;
//              cout << "\n";
                //if(curr->comment.compare("")!=0){

                myfile << i;
                s = "\t\t" + curr->label + "\t\t" + curr->opcode + "\t\t"
                                + curr->operand + "\t\t" + curr->comment + "\t\t"
                                + curr->objectCode + "\n" + curr->error + "\n";
                myfile << "\t\t";

                myfile << hex << curr->address;
//              myfile
//                              << "\t" + curr->label + "\t" + curr->opcode + "\t"
//                                              + curr->operand +"\t"+curr->comment;
//
//              myfile << "\n"+curr->error + "\n";

                myfile << s;
                //      cout << s;
                s = "";
                //if(error.compare("")==0){
                // }
                //}else{
                //}

        }

        myfile.close();

}
int Pass2::calcForBaseAndPC(int lineNo, string operand, int pc) {

        int address = -1;
        if (operand.compare("*") != 0) {
                address = symbolTable[operand].getAddress();

                if (address != -1) {
                        if (symbolTable[operand].getValue() != -1) {
                                address = symbolTable[operand].getValue();
                        }
                } else {
                        //      input[lineNo]->error = "**Error symbol not defined";
                }
        } else {
                address = input[lineNo]->address;

        }

        if (flag.b == 1) {
                //use base
                //address = symbolTable[operand].getAddress();
                address = address - baseValue;
                if (address > MAX_HEX_12 || address < 0) {
                        input[lineNo]->error = "**Error out of memory";
                }
        } else if (flag.p == 1) {
                //use pc

                address = address - pc;
                if (address > MAX_HEX_11 || address < (-1 * MAX_HEX_11)) {
                        input[lineNo]->error = "**Error out of memory";
                }
        }

        return address;
}
int Pass2::calcFormat4(int lineNo, string operand) {
        int address = -1;
        if (operand.compare("*") == 0) {
                address = input[lineNo]->address;
        } else {

                if (address == -1) {
                        //try to get the symbol from the symbol table
                        LabelObject label = symbolTable[operand];
                        address = label.getAddress();
                        if (address != -1) {
                                if (symbolTable[operand].getValue() != -1) {
                                        address = symbolTable[operand].getValue();
                                }
                                if (address > MAX_DEC_20) {
                                        input[lineNo]->error = "**Error out of memory";
                                }
                        } else {
                                // try to parse the operand ( absolute address)
                                std::stringstream ss(operand);
                                ss >> hex >> address;
                                if (ss.fail()) {
                                        // not an integer
                                        address = -1;
                                }
                                if (address > MAX_HEX_20) {
                                        input[lineNo]->error = "**Error out of memory";
                                }
                                //input[lineNo]->error = "**Error symbol not defined";
                        }
                }

        }
        if (address == -1) {
                input[lineNo]->error = "**Error symbol not defined";
        }
        return address;
}

int Pass2::calculateDiff(int n) {
        Line* line = input[n];
        int loctRR = input[n + 1]->address;
        string operand = line->operand;
        int tA;
        if (operand == "*") {
                tA = line->address;
        } else {
                if (operand.at(0) == '#') {
                        operand = operand.substr(1, operand.size());
                } else if (operand.at(operand.size() - 2) == ','
                                && operand.at(operand.size() - 1) == 'X') {
                        operand = operand.substr(0, operand.find(',', 0));
//                      cout << operand;
                }
                LabelObject label = symbolTable[operand];
                tA = label.getAddress();
        }
//      cout << "TA : ";
//      cout << tA;
//      cout << " loc";
//      cout << loctRR;
        return tA - loctRR;
}
/**
 * 0 if PC , 1 if Base , -1 if not base not PC
 */
int Pass2::checkBaseOrPC(int n) {
        int temp = calculateDiff(n);
        // base is already not valid
        if (baseValue == -1) {
                //check PC
                if (temp > 2047 || temp < -2048) {
                        return -1;
                } else
                        return 0;
        } else {
                //base is already valid
                //check PC
                if (temp > 2047 || temp < -2048) {
                        //check base
                        if (temp < 0 || temp > 4059)
                                return -1;
                        else
                                return 1;
                } else
                        return 0;
        }

}
bool Pass2::checkIndexed(int n) {
        Line* line = input[n];
        string operand = line->operand;
        if (operand != "*")
                if (operand.at(operand.size() - 2) == ','
                                && operand.at(operand.size() - 1) == 'X')
                        return true;
        return false;
}
vector<int> Pass2::calcFormat2(Line *curr) {
        string operand = curr->operand;
        vector<int> objectCode;
        string operation = curr->opcode;
        int opcpde = checker->getSymbolOpCode(operation);
        objectCode.push_back(opcpde);
        string r1 = operand.substr(0, 1);
        int r1O = checker->getSymbolOpCode(r1);
        objectCode.push_back(r1O);
        string r2 = operand.substr(2, 2);
        int r2O = checker->getSymbolOpCode(r2);
        objectCode.push_back(r2O);
        string s = convertToObjectCode(objectCode, 2);
//      cout << "ob";
//      cout << s;
//      cout << "finish";
        return objectCode;
}

string Pass2::convertToObjectCode(vector<int> input, int format) {
        string objectCode;
        int first = input[0];
        int second = input[1];
        int third = input[2];
        //convert object code and NI
        std::stringstream firstS;
        firstS << hex << first;
        string opcode = firstS.str();
        if (opcode.size() < 2) {
                //      int noOfLoop = 2 - opcode.size();
                //      for (int i = 0; i < noOfLoop; ++i) {
                opcode = "0" + opcode;
                //      }

        }

        if (format == 1) {
                objectCode = opcode;
        } else if (format == 2) {
                std::stringstream secondS;
                secondS << hex << second;
                string r1 = secondS.str();
                std::stringstream thirdS;
                thirdS << hex << third;
                string r2 = thirdS.str();
                objectCode = opcode + r1 + r2;
        } else if (format == 3) {
                //convert second element (x b p e )
                std::stringstream secondS;
                secondS << hex << second;
                string flags = secondS.str();
                if (flags.size() < 2) {
                        //      int noOfLoop = 2 - opcode.size();
                        //      for (int i = 0; i < noOfLoop; ++i) {
                        flags = "0" + flags;
                        //      }

                }
                //disp
                std::stringstream thirdS;
                thirdS << hex << third;
                string disp = thirdS.str();
                if (disp.size() < 3) {
                        int noOfLoop = 3 - disp.size();
                        for (int i = 0; i < noOfLoop; ++i) {
                                disp = "0" + disp;
                        }

                }
                if (disp.size() > 3) {
                        disp = disp.substr(disp.size() - 3, disp.size());
                }
                objectCode = opcode + flags + disp;
        } else if (format == 4) {
                //convert second element (x b p e )
                std::stringstream secondS;
                secondS << hex << second;
                string flags = secondS.str();
                if (flags.size() < 2) {
                        //      int noOfLoop = 2 - opcode.size();
                        //      for (int i = 0; i < noOfLoop; ++i) {
                        flags = "0" + flags;
                        //      }

                }
                std::stringstream thirdS;
                thirdS << hex << third;
                string disp = thirdS.str();
                if (disp.size() < 2) {
                        int noOfLoop = 5 - disp.size();
                        for (int i = 0; i < noOfLoop; ++i) {
                                disp = "0" + disp;
                        }

                }
                objectCode = opcode + flags + disp;
        }
        return objectCode;

}

string Pass2::getOperand(Line* line) {
        string operand = line->operand;
        int pos = operand.find(',', 0);

        return operand.substr(0, pos);
}

void Pass2::setFlags(int no) {
        Line* n = input[no];
        string operand = n->operand;
//      cout << operand;
        //check indexed
        if (operand != "") {
                if (checkIndexed(no)) {

                        flag.x = 1;
                } else
                        flag.x = 0;
                //check immediate
                if (operand.at(0) == '#') {
                        flag.i = 1;
                }
                //check indirect
                if (operand.at(0) == '@') {
                        flag.n = 1;
                }
                //default case
                if (flag.n == 0 && flag.i == 0) {
                        flag.n = 1;
                        flag.i = 1;
                }
                //check format 4
                if (n->format == 4) {
                        flag.e = 1;
                } else if (n->format == 3) {
                        //check if locTRR or base
                        if (checkBaseOrPC(no) == 0)
                                flag.p = 1;
                        else if (checkBaseOrPC(no) == 1)
                                flag.b = 1;

                }
        }
}
void Pass2::setObjectProgram() {
        Line* line;
        int loc = 0;
        int disp;
        int opcode;

        for (int i = 0; i < input.size(); ++i) {
                string s;
                vector<int> vector;
                line = input.at(i);
                string operation = line->opcode;
                string operand = line->operand;
                //line comment
                if (operation == "BYTE" || operation == "WORD") {
                        std::stringstream ss;
                        ss << hex << LocationCounter::calcWordAndByte(operand, false);
                        string s1 = ss.str();
                        line->objectCode = s1;
                }
                // no object code
                if (line->label == "*" || line->format == 0 || operation == "START"
                                || operation == "RESW" || operation == "RESB"
                                || operation == "END" || operation == "BASE") {

                        loc = line->address;
                        //check base
                        if (operation == "BASE") {
                                // if base statement is true
                                if (baseValue == symbolTable[operand].getAddress())
                                        base = true;
                                else {
                                        base = false;
                                        line->error =
                                                        line->error
                                                                        + " BASE statement must has the same address which in B register";
                                }
                        }
                }
                //lines has object code
                else {
                        if (line->format == 3 || line->format == 4) {
                                if (operation == "LDB") {
                                        if (operand.at(0) != '#') {
                                                line->error = line->error + " Error in operand field";
                                        } else {

                                                string temp = operand.substr(1, operand.size());
                                                baseValue = symbolTable[temp].getAddress();
                                        }
                                }
                                setFlags(i);
                                disp = calcDisp(i);
                                opcode = checker->getSymbolOpCode(operation);

                                std::ostringstream temp1;
                                temp1 << flag.n << flag.i;
                                std::istringstream iss(temp1.str());
                                int temp;
                                iss >> temp;
                                int t = binaryToBase10(temp);
//                              cout << operation;
//                              cout << ">>>>";
//                              cout << opcode;
//                              cout << "   ";
                                int firstElement = t + opcode;
                                vector.push_back(firstElement);
                                std::ostringstream temp2;
                                temp2 << flag.x << flag.b << flag.p << flag.e;
                                std::istringstream ios(temp2.str());
//                              cout << "flag ";
//                              cout << flag.x;
                                ios >> temp;
                                int n = binaryToBase10(temp);
//                              cout << "yalhwity";
//                              cout << binaryToBase10(temp);
                                vector.push_back(n);
                                vector.push_back(disp);
                                for (int var = 0; var < vector.size(); ++var) {
//
//                                      cout << vector.at(var);
//                                      cout << "\t";

                                }
//                              cout << "\n";

                        } else if (line->format == 1) {
                                vector.push_back(calcFormat1(i));

                        } else if (line->format == 2) {
                                vector = calcFormat2(line);
                        }
                        s = convertToObjectCode(vector, line->format);

                        line->objectCode = s;
                }
                flag.n = 0;
                flag.i = 0;
                flag.x = 0;
                flag.b = 0;
                flag.p = 0;
                flag.e = 0;
        }

}
int Pass2::binaryToBase10(int n) {

        int output = 0;

        for (int i = 0; n > 0; i++) {

                if (n % 10 == 1) {
                        output += pow(2, i);
                }
                n /= 10;
        }

        return output;
}


//--------------------------------------- object program
string Pass2::int2hex(unsigned int dec)
{
  int i = 0;
  stack <int>remainder;
  string hex, temp;
  char hexDigits[] = { "0123456789abcdef" };

  if(dec == 0)
    hex = hexDigits[0];

  while (dec != 0)
  {
    remainder.push(dec % 16);
    dec /= 16;
    ++i;
  }

  while (i != 0)
  {
    if (remainder.top() > 15)
    {
      temp = int2hex(remainder.top());
      hex += temp;
    }
    hex.push_back(hexDigits[remainder.top()]);
    remainder.pop();
    --i;
  }
  return hex;
}


string Pass2:: int2string( int number)
{
    ostringstream convert;   // stream used for the conversion

    convert << number;      // insert the textual representation of 'Number' in the characters in the stream

    return convert.str();
}



string Pass2::generateStart(string name,int start,int end)
{
    int nameLength = name.length();
    // set the rigth name
    if(nameLength < 6)
    {
        string y=name;
        for(int i=nameLength+1;i<=6;i++)
        {
            y +=' ';
        }
        name =y;
    }
    else if( nameLength >6)
    {
        string y="";
        for(int i=0;i<=6;i++)
        {
            y += y.at(i);
        }
        name = y;
    }


    string startS = int2hex(start);
    int startLength = startS.length();
    // set the rigth start
    if(startLength < 6)
    {
       string y="";
        for(int i=startLength+1;i<=6;i++)
        {
            y +='0';
        }
        y += startS;
        startS =y;
    }
    else if(startLength >6)
    {
         cout << "error";
    }



    int programeLength = end - start;
    string hexNumber = int2hex(programeLength);
    int hexLength = hexNumber.length();
    // set the rigth programeLength
    if(hexLength >6)
    {

         cout<< "error --> too long programe";
    }
    else if(hexLength <6)
    {
        string y="";
        for(int i=hexLength+1;i<=6;i++)
        {
            y += '0';
        }
        y += hexNumber;
        hexNumber = y;
    }



   string answer = "H"+ name +",,"+ startS +",,"+hexNumber;
   return answer;
}


string Pass2::modificationRecored(int start,int currentAddress)
{
    int next = start+currentAddress;
     string startS = int2hex(next);
                  int startLength = startS.length();
                 // set the rigth start
                  if(startLength < 6)
                  {
                    string y="";
                    for(int i=startLength+1;i<=6;i++)
                  {
                    y +='0';
                  }
                  y += startS;
                   startS =y;
                  }
                  else if(startLength >6)
                  {
                     cout << "error";
                  }
    string x = "M"+startS+"05";
    return x;
}


void  Pass2::objectPrograme(vector<Line> input)
{
    int modificationNumber=0;;
    string modification[input.size()];
    int textRecored =0;
    string textRecoreds[input.size()];
    string progName;
    int start=0;;
    int end=0;
    vector<string> lines;
    for(int i=0;i<input.size();i++)
    {
          string op = input.at(i).opcode;
          if(op.compare("START")==0)
          {
            start = input.at(i).address;
            end =input.at(i).address;
            progName = input.at(i).label;
          }
          else if(op.compare("END")==0)
          {
            end = input.at(i-1).address+1;
            break;
          }
          else
          {
              string lineName;
              string objectC = input.at(i).objectCode;
              if(objectC.compare("")!=0)
              {
                  lineName = "T";
                  int length = objectC.size();
                  int startT = input.at(i).address;
                  int endT = input.at(i).address;
                  if(input.at(i).format == 4)
                  {
                      string m = modificationRecored(start,input.at(i).address);
                      modification[modificationNumber] =m;
                      modificationNumber++;
                  }
                  bool cont = false;
                  for(int j=i+1;j<input.size();j++)
                  {
                      string next = input.at(j).objectCode;
                      string op1 = input.at(j).opcode;
                      int le = next.length();
                      if(op1.compare("END")==0||op1.compare("RESW")==0 ||op1.compare("RESB")==0)
                      {
                          cont = true;
                          int plength = endT-startT;
                          string startS2 = int2hex(startT);
                  int startLength2 = startS2.length();
                 // set the rigth start
                  if(startLength2 < 6)
                  {
                    string y="";
                    for(int i=startLength2+1;i<=6;i++)
                  {
                    y +='0';
                  }
                  y += startS2;
                   startS2=y;
                  }
                  else if(startLength2 >6)
                  {
                     cout << "error";
                  }
                          lineName += ",,"+startS2+",,"+ int2hex(plength)+",,"+objectC;
                         textRecoreds[textRecored] =  lineName;
                         textRecored++;

                          i=j-1;
                          break;
                      }
                      else if(op1.compare("BASE")==0)
                      {
                      }
                      else if(length+le>60)
                      {
                          i=j-1;
                          break;
                      }
                      else
                      {
                           if(input.at(j).format == 4)
                        {
                         string m = modificationRecored(start,input.at(j).address+1);
                         modification[modificationNumber] =m;
                         modificationNumber++;
                       }
                          objectC += input.at(j).objectCode;
                          length = objectC.length();
                          int m= j+1;
                          if(m < input.size())
                           endT = input.at(j+1).address;
                          i=j;
                      }
                  }
                  if(!cont)
                  {
                  int plength = endT -startT;
                  string startS = int2hex(startT);
                  int startLength = startS.length();
                 // set the rigth start
                  if(startLength < 6)
                  {
                    string y="";
                    for(int i=startLength+1;i<=6;i++)
                  {
                    y +='0';
                  }
                  y += startS;
                   startS =y;
                  }
                  else if(startLength >6)
                  {
                     cout << "error";
                  }


                  //-------------------------------------------------


                 string startS1 = int2hex(plength);
                  int startLength1 = startS1.length();
                 // set the rigth start
                  if(startLength1 < 2)
                  {
                    string y="";
                    for(int i=startLength1+1;i<=2;i++)
                  {
                    y +='0';
                  }
                  y += startS1;
                   startS1 =y;
                  }
                  else if(startLength1 >2)
                  {
                     cout << "error";
                  }

                  //---------------------------------------------------
                  lineName += ",,"+startS +",,"+startS1+",,"+objectC;
                  textRecoreds[textRecored] =  lineName;
                  textRecored++;

                  }

              }
          }
    }
    ofstream myfile;
    myfile.open ("objectPrograme.txt");
    string startTe = generateStart(progName,start,end);
    myfile << startTe;
    myfile << "\n";
    for(int i=0;i<textRecored;i++)
    {
        myfile << textRecoreds[i];
        myfile << "\n";
    }
    for(int i=0;i<modificationNumber;i++)
    {
        myfile << modification[i];
        myfile << "\n";
    }
     string startS = int2hex(start);
    int startLength = startS.length();
    // set the rigth start
    if(startLength < 6)
    {
       string y="";
        for(int i=startLength+1;i<=6;i++)
        {
            y +='0';
        }
        y += startS;
        startS =y;
    }
    string endTe = "E"+startS;
    myfile << endTe;
    myfile << "\n";
    myfile.close();
}
int main() {

        int value = 0;
        char *oper2 = "c'eof'";
        char *c;
        value = strtol(oper2, &c, 16);
        cout << value;
        Pass2 pass;
        pass.writeFile("test");

        return 0;
}

//--------------------------------------------------------------



